#!/bin/bash
# 修复Page Cache脏页堆积(刷盘延迟)的智能运维脚本
FIX_CODE_ID="dirty-page-accumulation"

# 定义配置文件路径
SYSCTL_CONF="/etc/sysctl.conf"
SYSCTL_D_DIR="/etc/sysctl.d/"

# 备份文件函数
backup_file() {
    local file="$1"
    if [ -f "$file" ]; then
        local backup="${file}.bak.$(date +%Y%m%d%H%M%S)"
        cp -a "$file" "$backup"
        echo "已备份配置文件: $backup"
    fi
}

# 获取当前脏页配置
get_current_config() {
    echo "当前脏页相关配置："
    sysctl vm.dirty_ratio
    sysctl vm.dirty_background_ratio
    sysctl vm.dirty_writeback_centisecs
    sysctl vm.dirty_expire_centisecs
    echo
}

# 临时调整脏页参数（立即生效，重启后失效）
apply_temporary_fix() {
    echo "应用临时修复，调整脏页参数..."
    
    # 降低脏页比例阈值，触发更早的刷盘操作
    sysctl -w vm.dirty_ratio=10
    sysctl -w vm.dirty_background_ratio=5
    
    # 缩短脏页写回和过期时间（单位：厘秒，1秒=100厘秒）
    sysctl -w vm.dirty_writeback_centisecs=500  # 5秒
    sysctl -w vm.dirty_expire_centisecs=2000    # 20秒
    
    echo "临时修复已应用"
    echo
}

# 永久调整脏页参数（重启后依然有效）
apply_permanent_fix() {
    echo "应用永久修复，配置将在重启后保持..."
    
    # 创建配置文件（优先使用sysctl.d目录）
    local config_file="${SYSCTL_D_DIR}99-dirty-pages.conf"
    
    # 如果sysctl.d目录不存在则使用sysctl.conf
    if [ ! -d "$SYSCTL_D_DIR" ]; then
        config_file="$SYSCTL_CONF"
        backup_file "$config_file"
    fi
    
    # 移除已有配置
    if [ -f "$config_file" ]; then
        sed -i '/^vm.dirty_ratio/d' "$config_file"
        sed -i '/^vm.dirty_background_ratio/d' "$config_file"
        sed -i '/^vm.dirty_writeback_centisecs/d' "$config_file"
        sed -i '/^vm.dirty_expire_centisecs/d' "$config_file"
    fi
    
    # 添加新配置
    echo "# 修复脏页堆积问题 - 由$(basename $0)脚本添加" >> "$config_file"
    echo "vm.dirty_ratio = 10" >> "$config_file"
    echo "vm.dirty_background_ratio = 5" >> "$config_file"
    echo "vm.dirty_writeback_centisecs = 500" >> "$config_file"
    echo "vm.dirty_expire_centisecs = 2000" >> "$config_file"
    
    # 使永久配置生效
    sysctl -p "$config_file" &> /dev/null
    
    echo "永久修复已应用，配置文件：$config_file"
    echo
}

# 手动触发脏页刷盘
force_writeback() {
    echo "手动触发脏页刷盘操作..."
    
    # 方法1：使用sync命令
    sync
    echo "sync命令执行完成"
    
    # 方法2：通过proc文件系统触发
    if [ -w "/proc/sys/vm/drop_caches" ]; then
        echo 3 > /proc/sys/vm/drop_caches
        echo "已触发页缓存刷新"
    fi
    
    echo "脏页刷盘操作完成"
    echo
}

# 检查当前脏页状态
check_dirty_pages() {
    echo "当前脏页状态："
    
    # 计算脏页数量和比例
    local total_pages=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    local dirty_pages=$(grep Dirty /proc/meminfo | awk '{print $2}')
    local writeback_pages=$(grep Writeback /proc/meminfo | awk '{print $2}')
    
    local dirty_ratio=$(( dirty_pages * 100 / total_pages ))
    local writeback_ratio=$(( writeback_pages * 100 / total_pages ))
    
    echo "总内存页数: $total_pages kB"
    echo "脏页数量: $dirty_pages kB ($dirty_ratio%)"
    echo "正在写回的页数: $writeback_pages kB ($writeback_ratio%)"
    echo
}

# 主逻辑
echo "开始处理Page Cache脏页堆积(刷盘延迟)问题..."
echo "=========================================="

# 检查当前状态
check_dirty_pages
get_current_config

# 执行修复操作
apply_temporary_fix
apply_permanent_fix
force_writeback

# 检查修复后的状态
echo "修复后的脏页配置："
get_current_config

echo "=========================================="
echo "Page Cache脏页堆积问题修复完成"
echo "系统将更早地将脏页写入磁盘，减少延迟"
exit 0
